package com.ccx.demo.business.common.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.ccx.demo.business.common.vo.FileInfo;
import com.utils.enums.Code;
import com.utils.util.FCopy;
import com.utils.util.FPath;
import com.utils.util.FWrite;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.BufferedReader;
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.channels.SeekableByteChannel;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.List;
import java.util.Optional;

import static java.nio.charset.StandardCharsets.UTF_8;

/**
 * 磁盘读写服务接口
 *
 * @author 谢长春 2020-11-13
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class DiskFileService implements IFileService {

    @SneakyThrows
    @Override
    public <T extends FileInfo> T write(final MultipartFile uploadFile, final T fileInfo, final boolean replace) {
        Code.A00001.assertNonBlank(fileInfo.getPath(), "必须指定存储路径");
        if (log.isInfoEnabled()) {
            log.info("上传文件路径【{}】：{}", fileInfo.getName(), fileInfo.getPath());
        }
        final FPath localFile = FPath.of(fileInfo.getPath());
        if (replace || !localFile.exist()) { // 将文件写入到本地
            localFile.mkdirsParent();
            uploadFile.transferTo(localFile.file());
            localFile.chmod();
        }
        return fileInfo;
    }

    @SneakyThrows
    @Override
    public <T extends FileInfo> T write(final byte[] bytes, final T fileInfo, final boolean replace) {
        if (log.isInfoEnabled()) {
            log.info("上传文件路径【{}】：{}", fileInfo.getName(), fileInfo.getPath());
        }
        Code.A00001.assertNonBlank(fileInfo.getPath(), "必须指定存储路径");
        final FPath to = FPath.of(fileInfo.getPath());
        if (replace || !to.exist()) {
            to.mkdirsParent();
            FWrite.of(fileInfo.getPath()).write(bytes);
        }
        return fileInfo;
    }

    @SneakyThrows
    @Override
    public <T extends FileInfo> T write(final File file, final T fileInfo, final boolean replace) {
        if (log.isInfoEnabled()) {
            log.info("上传文件路径【{}】：{}", fileInfo.getName(), fileInfo.getPath());
        }
        Code.A00001.assertNonBlank(fileInfo.getPath(), "必须指定存储路径");
        final File to = new File(fileInfo.getPath());
        if (replace || !to.exists()) {
            FCopy.ofDefault()
                    .from(file)
                    .to(to)
                    .copyTo();
        }
        return fileInfo;
    }
//
//    @SneakyThrows
//    @Override
//    public <T extends FileInfo> T write(final BufferedInputStream bufferedInputStream, final T fileInfo, final boolean replace) {
////        @Cleanup final BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
////        bufferedInputStream.mark(0);
////        fileInfo.setName(fileName.getName());
////        fileInfo.setUname(fileName.toMd5Uname(bufferedInputStream));
//        if (log.isInfoEnabled()) {
//            log.info("上传文件路径【{}】：{}", fileInfo.getName(), fileInfo.getPath());
//        }
//        Code.A00001.assertNonBlank(fileInfo.getPath(), "必须指定存储路径");
//        final FPath to = FPath.of(fileInfo.getPath());
//        if (replace || !to.exist()) {
////            bufferedInputStream.reset();
//            to.mkdirsParent(); // 创建目录
//            Files.copy(bufferedInputStream, to.get(), StandardCopyOption.REPLACE_EXISTING);
//            to.chmodFile(); // 文件授权
//        }
//        return fileInfo;
//    }

    @Override
    public Optional<String> readString(final FileInfo file) {
        return Optional.ofNullable(FPath.of(file.getPath()).read()).filter(StringUtils::isNoneBlank);
    }

    @Override
    public <T extends FileInfo, R> Optional<R> readJson(final T file, final TypeReference<R> typeReference) {
        return readString(file).map(jsonText -> JSON.parseObject(jsonText, typeReference));
    }

    @Override
    public <T extends FileInfo, R> Optional<R> readJsonObject(final T file, final Class<R> clazz) {
        return readString(file).map(jsonText -> JSON.parseObject(jsonText, clazz));
    }

    @Override
    public <T extends FileInfo, R> Optional<List<R>> readJsonArray(final T file, final Class<R> clazz) {
        return readString(file).map(jsonText -> JSON.parseArray(jsonText, clazz));
    }

    @SneakyThrows
    @Override
    public byte[] readBytes(final FileInfo file) {
        return Files.readAllBytes(Paths.get(file.getPath()));
    }

    @SneakyThrows
    @Override
    public BufferedReader readStream(final FileInfo file) {
        return Files.newBufferedReader(Paths.get(file.getPath()), UTF_8);
    }

    @SneakyThrows
    @Override
    public InputStream readInputStream(final FileInfo file) {
        return Files.newInputStream(Paths.get(file.getPath()), StandardOpenOption.READ);
    }

    @SneakyThrows
    @Override
    public SeekableByteChannel readChannel(final FileInfo file) {
        return Files.newByteChannel(Paths.get(file.getPath()), StandardOpenOption.READ);
    }

    @SneakyThrows
    @Override
    public void copy(final FileInfo file, final String absolutePath) {
        Files.copy(Paths.get(file.getPath()), Paths.get(absolutePath));
        FPath.of(absolutePath).chmod(644);
    }

//    @SneakyThrows
//    @Override
//    public void copy(final FileInfo file,final String absolutePath) {
//        @Cleanup final FileChannel fromFileChannel = FileChannel.open(Paths.get(file.getPath()), EnumSet.of(StandardOpenOption.READ));
//        @Cleanup final FileChannel toFileChannel = FileChannel.open(Paths.get(absolutePath), EnumSet.of(StandardOpenOption.CREATE, StandardOpenOption.WRITE));
//        fromFileChannel.transferTo(0L, fromFileChannel.size(), toFileChannel);
//        FPath.of(absolutePath).chmod(644);
//    }

    @SneakyThrows
    @Override
    public void copy(final FileInfo file, final OutputStream outputStream) {
        Files.copy(Paths.get(file.getPath()), outputStream);
    }
}
